package taboo

import (
	"strings"
)

// MatchTree 精确匹配搜索树，用于对指定列表中的字符串进行精确匹配
type MatchTree struct {
	words []*word
	root  *node
}

type word struct {
	S     string
	Runes []rune
	Len   int
}

type node struct {
	Char       rune
	MatchWords int
	Children   map[rune]*node
}

func newNode(char rune, match int) *node {
	return &node{
		Char:       char,
		MatchWords: match,
		Children:   make(map[rune]*node),
	}
}

// NewTree 根据传入敏感词列表创建一个新的敏感词匹配树
func NewTree(t []string) *MatchTree {
	tree := &MatchTree{
		words: nil,
		root:  newNode(0, -1),
	}

	for id, w := range t {
		w = strings.ToLower(strings.TrimSpace(w))
		word := &word{S: w, Runes: []rune(w)}
		tree.words = append(tree.words, word)
		word.Len = len(word.Runes)

		curr := tree.root
		for i, r := range word.Runes {
			child, ok := curr.Children[r]
			if !ok {
				child = newNode(r, -1)
				curr.Children[r] = child
			}
			if i == len(word.Runes)-1 {
				if child.MatchWords != -1 {
					//log.Printf(`taboo 库初始化：有重复的敏感词 [%v][%v] -- [%v][%v]`, child.MatchWords, tabooWords[child.MatchWords].S, id, word.S)
				} else {
					child.MatchWords = id
				}
				break
			}
			curr = child
		}
	}
	return tree
}

// Find 找到输入的文本中的敏感词，最多匹配 n 次
func (tree *MatchTree) Find(text string, n int) []Match {
	text = strings.ToLower(strings.TrimSpace(text))
	runes := []rune(text)
	result := tree.findMatches(runes, n)
	matches := []Match{}
	for _, res := range result {
		matches = append(matches, Match{
			Start: res[0],
			End:   res[1],
			Word:  tree.words[res[2]].S,
		})
	}
	return matches
}

// FindAll 找到输入的文本中的全部敏感词
func (tree *MatchTree) FindAll(text string) []Match {
	return tree.Find(text, -1)
}

// Contains 输入的文本中是否包含敏感词
func (tree *MatchTree) Contains(text string) bool {
	result := tree.Find(text, 1)
	return len(result) > 0
}

// ReplaceAll 替换全部敏感词为repl
func (tree *MatchTree) ReplaceAll(text, repl string) string {
	text = strings.ToLower(strings.TrimSpace(text))
	runes := []rune(text)
	result := tree.findMatches(runes, -1)
	replRunes := []rune(repl)
	place := 0
	for _, index := range result {
		for i := index[0]; i < index[1]; i++ {
			runes[i] = replRunes[place]
			place++
			if place >= len(replRunes) {
				place = 0
			}
		}
	}
	return string(runes)
}

// findMatchesFormStart 传入一个字符数组runes， 在匹配树中找到与指定字符数组 runes中 从[0-len(runes)]任意索引开始能完全匹配的屏蔽词
// 返回其在传入的字符数组runes中的起始位置、结束位置、和匹配的屏蔽词的索引
// 查找最多 n 个匹配，若 n<=0 则查找所有匹配
func (tree *MatchTree) findMatches(runes []rune, n int) [][3]int {
	result := make([][3]int, 0)
	for i := 0; i < len(runes); i++ {
		left := -1
		if n > 0 {
			left = n - len(result)
		}
		res := tree.findMatchesFormStart(runes[i:], left)
		for _, index := range res {
			index[0] += i
			index[1] += i
			result = append(result, index)
		}
		if n > 0 && len(result) >= n {
			return result
		}
	}
	return result
}

// findMatchesFormStart 传入一个字符数组runes， 在匹配树中找到与指定字符数组从开头开始能完全匹配的屏蔽词
// 返回其在传入的字符数组runes中的起始位置(恒定为0)、结束位置、和匹配的屏蔽词的索引
// 查找最多 n 个匹配，若 n<=0 则查找所有匹配
func (tree *MatchTree) findMatchesFormStart(runes []rune, n int) [][3]int {
	result := make([][3]int, 0)
	parent := tree.root
	for i, r := range runes {
		curr, ok := parent.Children[r]
		if !ok || curr == nil {
			return result
		}
		if curr.MatchWords != -1 {
			result = append(result, [3]int{0, i + 1, curr.MatchWords})
			if n > 0 && len(result) >= n {
				return result
			}
		}
		parent = curr
	}
	return result
}
